#include "glWorld.h"
#include "../rtCommon/Trace.h"

//#ifdef _DEBUG
#define USE_BENCHMARK
//#endif
#include "../rtCommon/Benchmark.h"

CglWorld::CglWorld()
{
	Null();
}

CglWorld::~CglWorld()
{
	Release();
}

void CglWorld::Null()
{
	m_nMesh = 0;
	m_pMesh = NULL;
}

void CglWorld::Create(CXobjIO ** ppXobj, uint32 nXobj)
{
	m_nMesh = nXobj;

	posO.Create(m_nMesh+1, BUFFER_H_AND_D);
	clrO.Create(m_nMesh+1, BUFFER_H_AND_D);
	nmlO.Create(m_nMesh+1, BUFFER_H_AND_D);
	tntO.Create(m_nMesh+1, BUFFER_H_AND_D);
	bntO.Create(m_nMesh+1, BUFFER_H_AND_D);
	uv_O.Create(m_nMesh+1, BUFFER_H_AND_D);

	posIdxO.Create(m_nMesh+1, BUFFER_H_AND_D);
	clrIdxO.Create(m_nMesh+1, BUFFER_H_AND_D);
	uv_IdxO.Create(m_nMesh+1, BUFFER_H_AND_D);

	posC.Create(m_nMesh+1, BUFFER_H_AND_D);
	clrC.Create(m_nMesh+1, BUFFER_H_AND_D);
	nmlC.Create(m_nMesh+1, BUFFER_H_AND_D);
	tntC.Create(m_nMesh+1, BUFFER_H_AND_D);
	bntC.Create(m_nMesh+1, BUFFER_H_AND_D);
	uv_C.Create(m_nMesh+1, BUFFER_H_AND_D);

	posIdxC.Create(m_nMesh+1, BUFFER_H_AND_D);
	clrIdxC.Create(m_nMesh+1, BUFFER_H_AND_D);
	uv_IdxC.Create(m_nMesh+1, BUFFER_H_AND_D);

	m_pMesh = new CglMesh[m_nMesh];

	uint32 * pPosO = posO.hPtrR();
	uint32 * pClrO = clrO.hPtrR();
	uint32 * pNmlO = nmlO.hPtrR();
	uint32 * pTntO = tntO.hPtrR();
	uint32 * pBntO = bntO.hPtrR();
	uint32 * pUV_O = uv_O.hPtrR();

	uint32 * pPosIdxO = posIdxO.hPtrR();
	uint32 * pClrIdxO = clrIdxO.hPtrR();
	uint32 * pUV_IdxO = uv_IdxO.hPtrR();

	uint32 * pPosC = posC.hPtrR();
	uint32 * pClrC = clrC.hPtrR();
	uint32 * pNmlC = nmlC.hPtrR();
	uint32 * pTntC = tntC.hPtrR();
	uint32 * pBntC = bntC.hPtrR();
	uint32 * pUV_C = uv_C.hPtrR();

	uint32 * pPosIdxC = posIdxC.hPtrR();
	uint32 * pClrIdxC = clrIdxC.hPtrR();
	uint32 * pUV_IdxC = uv_IdxC.hPtrR();

	for (uint32 iMesh = 0; iMesh <= m_nMesh; iMesh++)
	{

		//////////////////////////////////////////////////////////////////////////
		/// compute offsets for mesh data
		//////////////////////////////////////////////////////////////////////////
		if (iMesh == 0)
		{
			pPosO   [iMesh] = 0;
			pClrO   [iMesh] = 0;
			pNmlO   [iMesh] = 0;
			pTntO   [iMesh] = 0;
			pBntO   [iMesh] = 0;
			pUV_O   [iMesh] = 0;

			pPosIdxO[iMesh] = 0;
			pClrIdxO[iMesh] = 0;
			pUV_IdxO[iMesh] = 0;
		}
		else
		{
			pPosO   [iMesh] = pPosO   [iMesh-1] + pPosC   [iMesh-1];
			pClrO   [iMesh] = pClrO   [iMesh-1] + pClrC   [iMesh-1];
			pNmlO   [iMesh] = pNmlO   [iMesh-1] + pNmlC   [iMesh-1];
			pTntO   [iMesh] = pTntO   [iMesh-1] + pTntC   [iMesh-1];
			pBntO   [iMesh] = pBntO   [iMesh-1] + pBntC   [iMesh-1];
			pUV_O   [iMesh] = pUV_O   [iMesh-1] + pUV_C   [iMesh-1];

			pPosIdxO[iMesh] = pPosIdxO[iMesh-1] + pPosIdxC[iMesh-1];
			pClrIdxO[iMesh] = pClrIdxO[iMesh-1] + pClrIdxC[iMesh-1];
			pUV_IdxO[iMesh] = pUV_IdxO[iMesh-1] + pUV_IdxC[iMesh-1];
		}

		//////////////////////////////////////////////////////////////////////////
		/// store count indices: last value in offset buffer marks total count
		//////////////////////////////////////////////////////////////////////////
		if (iMesh < m_nMesh)
		{
			pPosC   [iMesh] = (uint32) ppXobj[iMesh]->pos.size();
			pClrC   [iMesh] = (uint32) ppXobj[iMesh]->clr.size();
			pNmlC   [iMesh] = (uint32) ppXobj[iMesh]->nml.size();
			pTntC   [iMesh] = (uint32) ppXobj[iMesh]->tnt.size();
			pBntC   [iMesh] = (uint32) ppXobj[iMesh]->bnt.size();
			pUV_C   [iMesh] = (uint32) ppXobj[iMesh]->uv.size();

			pPosIdxC[iMesh] = (uint32) ppXobj[iMesh]->posIdx.size();
			pClrIdxC[iMesh] = (uint32) ppXobj[iMesh]->clrIdx.size();
			pUV_IdxC[iMesh] = (uint32) ppXobj[iMesh]->uvIdx.size();
		}
		else
		{
			pPosC   [iMesh] = pPosO   [iMesh];
			pClrC   [iMesh] = pClrO   [iMesh];
			pNmlC   [iMesh] = pNmlO   [iMesh];
			pTntC   [iMesh] = pTntO   [iMesh];
			pBntC   [iMesh] = pBntO   [iMesh];
			pUV_C   [iMesh] = pUV_O   [iMesh];

			pPosIdxC[iMesh] = pPosIdxO[iMesh];
			pClrIdxC[iMesh] = pClrIdxO[iMesh];
			pUV_IdxC[iMesh] = pUV_IdxO[iMesh];
		}
	}

	posO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	clrO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	nmlO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	tntO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	bntO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	uv_O.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);

	posIdxO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	clrIdxO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	uv_IdxO.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);

	posC.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	clrC.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	nmlC.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	tntC.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	bntC.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);
	uv_C.Memcpy(BUFFER_H_TO_D, 0, 0, BUFFER_R);

	//////////////////////////////////////////////////////////////////////////
	/// allocate buffers for matrix and materials
	//////////////////////////////////////////////////////////////////////////

	b_matrix.Create   (m_nMesh*4, BUFFER_H_AND_D);

	b_ambient.Create  (m_nMesh,   BUFFER_H_AND_D);
	b_diffuse.Create  (m_nMesh,   BUFFER_H_AND_D);
	b_specular.Create (m_nMesh,   BUFFER_H_AND_D);
	b_emission.Create (m_nMesh,   BUFFER_H_AND_D);
	b_shininess.Create(m_nMesh,   BUFFER_H_AND_D);

	//////////////////////////////////////////////////////////////////////////
	/// allocate resources according to count table
	//////////////////////////////////////////////////////////////////////////

	pos.Create   (pPosC   [m_nMesh], BUFFER_H_AND_B, true );
	clr.Create   (pClrC   [m_nMesh], BUFFER_H_AND_B, false);
	nml.Create   (pNmlC   [m_nMesh], BUFFER_H_AND_B, true );
	tnt.Create   (pTntC   [m_nMesh], BUFFER_H_AND_B, true );
	bnt.Create   (pBntC   [m_nMesh], BUFFER_H_AND_B, true );
	uv.Create    (pUV_C   [m_nMesh], BUFFER_H_AND_B, false);
	posIdx.Create(pPosIdxC[m_nMesh], BUFFER_H_AND_B, true ); // second buffer stores offsets
	clrIdx.Create(pClrIdxC[m_nMesh], BUFFER_H_AND_B, true );
	uvIdx.Create (pUV_IdxC[m_nMesh], BUFFER_H_AND_B, true );
	
	posIdxSwizzled.Create(pPosIdxC[m_nMesh], BUFFER_H_AND_B, false);

	posMeshId.Create   (pPosC[m_nMesh],    BUFFER_H_AND_D);
	posIdxMeshId.Create(pPosIdxC[m_nMesh], BUFFER_H_AND_D);

	uint32 nPrimitive = pPosIdxC[m_nMesh] / 3;

	float4 * pPos     = pos.hPtrR();
	float4 * pClr     = clr.hPtrR();
	float4 * pNml     = nml.hPtrR();
	float4 * pTnt     = tnt.hPtrR();
	float4 * pBnt     = bnt.hPtrR();
	float2 * pUV      = uv.hPtrR();
	int32  * pPosIdxR = posIdx.hPtrR();
	int32  * pPosIdxW = posIdx.hPtrW();
	int4   * pClrIdxR = clrIdx.hPtrR();
	int4   * pClrIdxW = clrIdx.hPtrW();
	int4   * pUVIdxR  = uvIdx.hPtrR();
	int4   * pUVIdxW  = uvIdx.hPtrW();

	uint32 * pPosMeshId = posMeshId.hPtrR();
	uint32 * pPosIdxMeshId = posIdxMeshId.hPtrR();
	
	for (uint32 iMesh = 0; iMesh < m_nMesh; iMesh++)
	{
		if (ppXobj[iMesh]->pos.size())   memcpy(pPos     + pPosO   [iMesh], &(ppXobj[iMesh]->pos[0]), pPosC   [iMesh]*sizeof(float4));	
		if (ppXobj[iMesh]->clr.size())   memcpy(pClr     + pClrO   [iMesh], &(ppXobj[iMesh]->clr[0]), pClrC   [iMesh]*sizeof(float4));
		if (ppXobj[iMesh]->nml.size())   memcpy(pNml     + pNmlO   [iMesh], &(ppXobj[iMesh]->nml[0]), pNmlC   [iMesh]*sizeof(float4)); 		
		if (ppXobj[iMesh]->tnt.size())   memcpy(pTnt     + pTntO   [iMesh], &(ppXobj[iMesh]->tnt[0]), pTntC   [iMesh]*sizeof(float4)); 
		if (ppXobj[iMesh]->bnt.size())   memcpy(pBnt     + pBntO   [iMesh], &(ppXobj[iMesh]->bnt[0]), pBntC   [iMesh]*sizeof(float4));		
		if (ppXobj[iMesh]->uv.size())    memcpy(pUV      + pUV_O   [iMesh], &(ppXobj[iMesh]->uv[0]),  pUV_C   [iMesh]*sizeof(float2)); 	
		
		if (ppXobj[iMesh]->posIdx.size()) memcpy(pPosIdxR + pPosIdxO[iMesh], &(ppXobj[iMesh]->posIdx[0]), pPosIdxC[iMesh]*sizeof(int)); 		
		if (ppXobj[iMesh]->clrIdx.size()) memcpy(pClrIdxR + pClrIdxO[iMesh], &(ppXobj[iMesh]->clrIdx[0]), pClrIdxC[iMesh]*sizeof(int4)); 
		if (ppXobj[iMesh]->uvIdx.size())  memcpy(pUVIdxR  + pUV_IdxO[iMesh], &(ppXobj[iMesh]->uvIdx[0]),  pUV_IdxC[iMesh]*sizeof(int4)); 

		m_pMesh[iMesh].pos.bDouble   (pos.bDouble()   );
		m_pMesh[iMesh].clr.bDouble   (clr.bDouble()   );
		m_pMesh[iMesh].nml.bDouble   (nml.bDouble()   );
		m_pMesh[iMesh].tnt.bDouble   (tnt.bDouble()   );
		m_pMesh[iMesh].bnt.bDouble   (bnt.bDouble()   );
		m_pMesh[iMesh].uv.bDouble    (uv.bDouble()    );
		m_pMesh[iMesh].posIdx.bDouble(posIdx.bDouble());
		m_pMesh[iMesh].clrIdx.bDouble(clrIdx.bDouble());
		m_pMesh[iMesh].uvIdx.bDouble (uvIdx.bDouble() );

		m_pMesh[iMesh].pos.offset   (BUFFER_R_AND_W, pPosO   [iMesh], pPosO   [iMesh]);
		m_pMesh[iMesh].clr.offset   (BUFFER_R_AND_W, pClrO   [iMesh], pClrO   [iMesh]);
		m_pMesh[iMesh].nml.offset   (BUFFER_R_AND_W, pNmlO   [iMesh], pNmlO   [iMesh]);
		m_pMesh[iMesh].tnt.offset   (BUFFER_R_AND_W, pTntO   [iMesh], pTntO   [iMesh]);
		m_pMesh[iMesh].bnt.offset   (BUFFER_R_AND_W, pBntO   [iMesh], pBntO   [iMesh]);
		m_pMesh[iMesh].uv.offset    (BUFFER_R_AND_W, pUV_O   [iMesh], pUV_O   [iMesh]);
		m_pMesh[iMesh].posIdx.offset(BUFFER_R_AND_W, pPosIdxO[iMesh], pPosIdxO[iMesh]);
		m_pMesh[iMesh].clrIdx.offset(BUFFER_R_AND_W, pClrIdxO[iMesh], pClrIdxO[iMesh]);
		m_pMesh[iMesh].uvIdx.offset (BUFFER_R_AND_W, pUV_IdxO[iMesh], pUV_IdxO[iMesh]);

		m_pMesh[iMesh].pos.hPtr     (BUFFER_R_AND_W, pos.hPtrR(),			pos.hPtrW()   );
		m_pMesh[iMesh].clr.hPtr			(BUFFER_R_AND_W, clr.hPtrR(),			clr.hPtrW()   );
		m_pMesh[iMesh].nml.hPtr			(BUFFER_R_AND_W, nml.hPtrR(),			nml.hPtrW()   );
		m_pMesh[iMesh].tnt.hPtr			(BUFFER_R_AND_W, tnt.hPtrR(),			tnt.hPtrW()   );
		m_pMesh[iMesh].bnt.hPtr			(BUFFER_R_AND_W, bnt.hPtrR(),			bnt.hPtrW()   );
		m_pMesh[iMesh].uv.hPtr			(BUFFER_R_AND_W, uv.hPtrR(),			uv.hPtrW()    );
		m_pMesh[iMesh].posIdx.hPtr	(BUFFER_R_AND_W, posIdx.hPtrR(),	posIdx.hPtrW());
		m_pMesh[iMesh].clrIdx.hPtr	(BUFFER_R_AND_W, clrIdx.hPtrR(),	clrIdx.hPtrW());
		m_pMesh[iMesh].uvIdx.hPtr		(BUFFER_R_AND_W, uvIdx.hPtrR(),		uvIdx.hPtrW() );

		m_pMesh[iMesh].pos.vbo			(BUFFER_R_AND_W, pos.vboR(),			pos.vboW()   );
		m_pMesh[iMesh].clr.vbo			(BUFFER_R_AND_W, clr.vboR(),			clr.vboW()   );
		m_pMesh[iMesh].nml.vbo			(BUFFER_R_AND_W, nml.vboR(),			nml.vboW()   );
		m_pMesh[iMesh].tnt.vbo			(BUFFER_R_AND_W, tnt.vboR(),			tnt.vboW()   );
		m_pMesh[iMesh].bnt.vbo			(BUFFER_R_AND_W, bnt.vboR(),			bnt.vboW()   );
		m_pMesh[iMesh].uv.vbo				(BUFFER_R_AND_W, uv.vboR(),				uv.vboW()    );
		m_pMesh[iMesh].posIdx.vbo		(BUFFER_R_AND_W, posIdx.vboR(),		posIdx.vboW());
		m_pMesh[iMesh].clrIdx.vbo		(BUFFER_R_AND_W, clrIdx.vboR(),		clrIdx.vboW());
		m_pMesh[iMesh].uvIdx.vbo		(BUFFER_R_AND_W, uvIdx.vboR(),		uvIdx.vboW() );

		m_pMesh[iMesh].pos.size   (pPosC   [iMesh]);
		m_pMesh[iMesh].clr.size   (pClrC   [iMesh]);
		m_pMesh[iMesh].nml.size   (pNmlC   [iMesh]);
		m_pMesh[iMesh].tnt.size   (pTntC   [iMesh]);
		m_pMesh[iMesh].bnt.size   (pBntC   [iMesh]);
		m_pMesh[iMesh].uv.size    (pUV_C   [iMesh]);
		m_pMesh[iMesh].posIdx.size(pPosIdxC[iMesh]);
		m_pMesh[iMesh].clrIdx.size(pClrIdxC[iMesh]);
		m_pMesh[iMesh].uvIdx.size (pUV_IdxC[iMesh]);

		//m_pMesh[iMesh].material = ppXobj[iMesh]->material;
		m_pMesh[iMesh].matrixWS = ppXobj[iMesh]->matrixWS;

		b_matrix[BUFFER_R][iMesh*4+0] = m_pMesh[iMesh].matrixWS.row[0];
		b_matrix[BUFFER_R][iMesh*4+1] = m_pMesh[iMesh].matrixWS.row[1];
		b_matrix[BUFFER_R][iMesh*4+2] = m_pMesh[iMesh].matrixWS.row[2];
		b_matrix[BUFFER_R][iMesh*4+3] = m_pMesh[iMesh].matrixWS.row[3];

		b_ambient  [BUFFER_R][iMesh] = m_pMesh[iMesh].material.ambient();
		b_diffuse  [BUFFER_R][iMesh] = m_pMesh[iMesh].material.diffuse();
		b_specular [BUFFER_R][iMesh] = m_pMesh[iMesh].material.specular();
		b_emission [BUFFER_R][iMesh] = m_pMesh[iMesh].material.emission();
		b_shininess[BUFFER_R][iMesh] = m_pMesh[iMesh].material.shininess();

		for (uint32 iPosO = pPosO[iMesh]; iPosO < pPosO[iMesh+1]; iPosO++)
		{
			pPosMeshId[iPosO] = iMesh;
		}	

		for (uint32 iPosIdxO = pPosIdxO[iMesh]; iPosIdxO < pPosIdxO[iMesh+1]; iPosIdxO++)
		{
			pPosIdxMeshId[iPosIdxO] = iMesh;

			pPosIdxW[iPosIdxO] = pPosIdxR[iPosIdxO] + pPosO[iMesh];
		}	

		for (uint32 iClrIdxO = pClrO[iMesh]; iClrIdxO < pClrO[iMesh+1]; iClrIdxO++)
		{
			pClrIdxW[iClrIdxO] = pClrIdxW[iClrIdxO] + pClrO[iMesh];
		}

		for (uint32 iUVIdxO = pUV_O[iMesh]; iUVIdxO < pUV_O[iMesh+1]; iUVIdxO++)
		{
			pUVIdxW[iUVIdxO] = pUVIdxR[iUVIdxO] + pUV_O[iMesh];
		}
	}

	uint32 * pPosIdxSwizzled = posIdxSwizzled.hPtrR();

	uint32 nPrim = posIdx.size() / 3;

	for (uint32 iPosIdx = 0; iPosIdx < posIdx.size(); iPosIdx+=3)
	{
		pPosIdxSwizzled[0*nPrim + iPosIdx/3] = pPosIdxW[iPosIdx+0];
		pPosIdxSwizzled[1*nPrim + iPosIdx/3] = pPosIdxW[iPosIdx+1];
		pPosIdxSwizzled[2*nPrim + iPosIdx/3] = pPosIdxW[iPosIdx+2];
	}

	b_ambient.Memcpy      (BUFFER_H_TO_D);	
	b_diffuse.Memcpy      (BUFFER_H_TO_D);
	b_specular.Memcpy     (BUFFER_H_TO_D);
	b_emission.Memcpy     (BUFFER_H_TO_D);
	b_shininess.Memcpy    (BUFFER_H_TO_D);

	b_matrix.Memcpy       (BUFFER_H_TO_D);

	posMeshId.Memcpy      (BUFFER_H_TO_D);
	posIdxMeshId.Memcpy   (BUFFER_H_TO_D); 

	posIdxSwizzled.Memcpy (BUFFER_H_TO_D);

	m_tree.Create( &pos, &posIdxSwizzled, &posIdx);
}

void CglWorld::Release()
{
	//////////////////////////////////////////////////////////////////////////
	/// TODO
	//////////////////////////////////////////////////////////////////////////

	pos.Release(BUFFER_H_AND_B);
	nml.Release(BUFFER_H_AND_B);
	tnt.Release(BUFFER_H_AND_B);
	bnt.Release(BUFFER_H_AND_B);
	clr.Release(BUFFER_H_AND_B);
	uv.Release (BUFFER_H_AND_B);
	posIdx.Release(BUFFER_H_AND_B);
	clrIdx.Release(BUFFER_H_AND_B);
	uvIdx.Release (BUFFER_H_AND_B);

	posO.Release(BUFFER_H_AND_D);
	posC.Release(BUFFER_H_AND_D);
	clrO.Release(BUFFER_H_AND_D);
	clrC.Release(BUFFER_H_AND_D);
	uv_O.Release(BUFFER_H_AND_D);
	uv_C.Release(BUFFER_H_AND_D);
	nmlO.Release(BUFFER_H_AND_D);
	nmlC.Release(BUFFER_H_AND_D);	
	tntO.Release(BUFFER_H_AND_D);
	tntC.Release(BUFFER_H_AND_D);	
	bntO.Release(BUFFER_H_AND_D);
	bntC.Release(BUFFER_H_AND_D);
	posIdxO.Release(BUFFER_H_AND_D);
	posIdxC.Release(BUFFER_H_AND_D);	
	clrIdxO.Release(BUFFER_H_AND_D);
	clrIdxC.Release(BUFFER_H_AND_D);
	uv_IdxO.Release(BUFFER_H_AND_D);
	uv_IdxC.Release(BUFFER_H_AND_D);

	posMeshId.Release(BUFFER_H_AND_D);
	posIdxMeshId.Release(BUFFER_H_AND_D);

	for (uint32 iMesh = 0; iMesh < m_nMesh; iMesh++)
	{
		m_pMesh[iMesh].pos.Null();
		m_pMesh[iMesh].clr.Null();
		m_pMesh[iMesh].nml.Null();
		m_pMesh[iMesh].tnt.Null();
		m_pMesh[iMesh].bnt.Null();
		m_pMesh[iMesh].uv.Null();
		m_pMesh[iMesh].posIdx.Null();
		m_pMesh[iMesh].clrIdx.Null();
		m_pMesh[iMesh].uvIdx.Null();
	}

	b_ambient.Release(BUFFER_H_AND_D);
	b_diffuse.Release(BUFFER_H_AND_D);
	b_specular.Release(BUFFER_H_AND_D);
	b_emission.Release(BUFFER_H_AND_D);
	b_shininess.Release(BUFFER_H_AND_D);

	b_matrix.Release(BUFFER_H_AND_D);

	posIdxSwizzled.Release(BUFFER_H_AND_D);
	posMeshId.Release(BUFFER_H_AND_D);
	posIdxMeshId.Release(BUFFER_H_AND_D);

	delete [] m_pMesh;

	Null();
}

void CglWorld::Update(uint8 uFlags)
{
	((CglMesh *) this)->Update(uFlags);
}

void CglWorld::Memcpy(uint8 dirFlags, uint8 cpyFlags)
{
	pos.Memcpy(dirFlags, 0, 0, cpyFlags);
	nml.Memcpy(dirFlags, 0, 0, cpyFlags);
	tnt.Memcpy(dirFlags, 0, 0, cpyFlags);
	bnt.Memcpy(dirFlags, 0, 0, cpyFlags);
}

void CglWorld::Swap()
{
	((CglMesh *) this)->Swap();

	for (uint32 iMesh = 0; iMesh < m_nMesh; iMesh++)
		m_pMesh[iMesh].Swap();
}

void CglWorld::TransformWS(uint8 exeFlags)
{
	if (exeFlags == EXEC_H)
	{ 
		float4 * pPosR = pos.hPtrR();
		float4 * pPosW = pos.hPtrW();

		float4 * pNmlR = nml.hPtrR();
		float4 * pNmlW = nml.hPtrW();

		float4 * pTntR = tnt.hPtrR();
		float4 * pTntW = tnt.hPtrW();

		float4 * pBntR = bnt.hPtrR();
		float4 * pBntW = bnt.hPtrW();

		uint32 * pMeshID = posMeshId.hPtrR();
		float4 * pMatrixWS = b_matrix.hPtrR();

		for (uint32 iV = 0; iV < pos.size(); iV++)
		{
			uint32 iMesh = pMeshID[iV];		

			matrix4x4 mWS;

			mWS.row[0] = pMatrixWS[iMesh*4+0];
			mWS.row[1] = pMatrixWS[iMesh*4+1];
			mWS.row[2] = pMatrixWS[iMesh*4+2];
			mWS.row[3] = pMatrixWS[iMesh*4+3];			
			
			if (pPosR != NULL && pPosW != NULL)
			{
				pPosW[iV] = pPosR[iV] * mWS;
			}

			if (pNmlR != NULL && pNmlW != NULL)
			{
				pNmlW[iV] = normalize4(Matrix4x4TransformDirectionR(pNmlR[iV], mWS));
			}

			if (pTntR != NULL && pTntW != NULL)
			{
				pTntW[iV] = normalize4(Matrix4x4TransformDirectionR(pTntR[iV], mWS));
			}

			if (pBntR != NULL && pBntW != NULL)
			{
				pBntW[iV] = normalize4(Matrix4x4TransformDirectionR(pBntR[iV], mWS));			
			}
		}
	}

	if (exeFlags == EXEC_D)
	{		
		pos.Map(BUFFER_R_AND_W);
		nml.Map(BUFFER_R_AND_W);
		tnt.Map(BUFFER_R_AND_W);
		bnt.Map(BUFFER_R_AND_W);

		pos.Memcpy(BUFFER_H_TO_D);
		nml.Memcpy(BUFFER_H_TO_D);
		tnt.Memcpy(BUFFER_H_TO_D);
		bnt.Memcpy(BUFFER_H_TO_D);

		Wrapper_TransformWS(pos.size(), b_matrix.size() / 4,
												posMeshId.dPtrR(), b_matrix.dPtrR(), 
												pos.dPtrW(), pos.dPtrR(), 
												nml.dPtrW(), nml.dPtrR(),
												tnt.dPtrW(), tnt.dPtrR(), 
												bnt.dPtrW(), bnt.dPtrR());

		pos.Unmap(BUFFER_R_AND_W);
		nml.Unmap(BUFFER_R_AND_W);
		tnt.Unmap(BUFFER_R_AND_W);
		bnt.Unmap(BUFFER_R_AND_W);
	}
}

void CglWorld::SceneAABB(uint8 exeFlags)
{
	CBenchmark aabbBench;
	if (exeFlags == EXEC_H)
	{
		float4 * pPosW = pos.hPtrW();

		aabbBench.Start("SceneAABB");

		if (pPosW != NULL)
		{
			m_tree.m_box.pmin = f3(pPosW[0]);
			m_tree.m_box.pmax = f3(pPosW[0]);

			for (uint32 iV = 1; iV < pos.size(); iV++)
			{
				m_tree.m_box.pmin = min(f3(pPosW[iV]), m_tree.m_box.pmin);
				m_tree.m_box.pmax = max(f3(pPosW[iV]), m_tree.m_box.pmax);
			}
		}

		aabbBench.End();
	}
}

void CglWorld::PrimitiveAABB(uint8 exeFlags)
{
	m_tree.PrimitiveAABB(exeFlags);
}

void CglWorld::Histogram(uint8 exeFlags)
{
}

void CglWorld::SortSplitCandidates(uint8 exeFlags)
{
}

void CglWorld::KdTree(uint8 exeFlags)
{

	m_tree.KdTree_p0(exeFlags);

	m_tree.KdTree_p1(exeFlags);
}

void CglWorld::BVHTree(uint8 exeFlags)
{

	m_tree.BVHTree_p0(exeFlags);

	m_tree.BVHTree_p1(exeFlags);
}

sf2Buffer * CglWorld::KdTree()
{
	return &m_tree.m_KdTree;
}

sf4Buffer * CglWorld::Position()
{
	return &pos;
}

si_Buffer * CglWorld::Face()
{
	return &posIdx;
}

void CglWorld::KdTrace(IN  sf4Buffer & rayO, 
											 IN  sf4Buffer & rayD, 
											 OUT sf_Buffer & tmin, 
											 OUT si2Buffer & id, 
											 OUT uc4Buffer & rgba8)
{
	//TransformWS   (EXEC_H);
	//PrimitiveAABB (EXEC_H);
	//SceneAABB     (EXEC_H);
	//KdTree        (EXEC_H);

	mpTraceKdTree(&m_tree, 
				        m_tree.m_box,
								rayO.hPtrR(), 
								rayD.hPtrR(),
								rayO.size(),
								pos.hPtrW(),
								posIdx.hPtrW(),
								m_tree.m_treeIdx.hPtrR(),
								tmin.hPtrR(), 
								id.hPtrR(), 
								rgba8.hPtrR());
}

void CglWorld::KdTrace(IN  float3    & orig,
											 IN  float3    & dir, 
											 IN  int         iRay,
											 OUT sf_Buffer & tmin, 
											 OUT si2Buffer & id, 
											 OUT uc4Buffer & rgba8)
{
	
	mpTraceKdTree(&m_tree, 
				        m_tree.m_box,
								orig, dir, iRay,
								pos.hPtrW(),
								posIdx.hPtrW(),
								m_tree.m_treeIdx.hPtrR(),
								tmin.hPtrR(), 
								id.hPtrR(), 
								rgba8.hPtrR());
}

void CglWorld::BVHTrace(IN  float3    & orig,
											  IN  float3    & dir, 
											  IN  int         iRay,
											  OUT sf_Buffer & tmin, 
											  OUT si2Buffer & id, 
											  OUT uc4Buffer & rgba8)
{
	
	mpTraceBVHTree(&m_tree, 
								 orig, dir, iRay,
								 pos.hPtrW(),
								 posIdx.hPtrW(),
								 m_tree.m_treeIdx.hPtrR(),
								 tmin.hPtrR(), 
								 id.hPtrR(), 
								 rgba8.hPtrR());
}

void CglWorld::BVHTrace(IN  sf4Buffer & rayO, 
											  IN  sf4Buffer & rayD, 
											  OUT sf_Buffer & tmin, 
											  OUT si2Buffer & id, 
											  OUT uc4Buffer & rgba8)
{
	//TransformWS   (EXEC_H);
	//PrimitiveAABB (EXEC_H);
	//SceneAABB     (EXEC_H);
	//BVHTree       (EXEC_H);

	mpTraceBVHTree(&m_tree, 
								 rayO.hPtrR(), 
								 rayD.hPtrR(), 
								 rayO.size(),
								 m_tree.pVertex(),
								 m_tree.pFace(),
								 m_tree.pPrimitive(),
								 tmin.hPtrR(), 
								 id.hPtrR(), 
								 rgba8.hPtrR());
}
//////////////////////////////////////////////////////////////////////////
/// Render methods
//////////////////////////////////////////////////////////////////////////
void CglWorld::RenderKdTree(int maxLevel, bool empty)
{
	glRenderKdTree(&m_tree, maxLevel, empty);
}

void CglWorld::RenderKdTreeLeaf(int maxLevel, bool empty)
{
	glRenderKdTreeLeaf(&m_tree, maxLevel, empty);
}

void CglWorld::RenderBVHTree(int maxLevel, bool empty)
{
	glRenderBVHTree(&m_tree, maxLevel, empty);
}

void CglWorld::RenderBVHTreeLeaf(int maxLevel, bool empty)
{
	glRenderBVHTreeLeaf(&m_tree, maxLevel, empty);
}

void CglWorld::RenderKdTreeRayTrace(const float3 & orig, const float3 & dir, int maxLevel, bool empty)
{
	glRenderKdTreeRayTrace(&m_tree, orig, dir, maxLevel, empty);
}

void CglWorld::RenderBVHTreeRayTrace(const float3 & orig, const float3 & dir, int maxLevel, bool empty)
{
	glRenderBVHTreeRayTrace(&m_tree, orig, dir, maxLevel, empty);
}